package com.example.zjx8969.myapplication.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;

/**
 * 色盘组件；
 * 用途：选择自定义组件所需要的；
 */
public class ColorPickerView extends View implements LifecycleObserver {
    public interface OnColorChangedListener {
        void onColorChanged(int color);

        void onStartDragging();

        void onStopDragging();
    }

    private static final float PI = 3.1415926f;

    private LinearGradient mSaturationShader;
    private LinearGradient mBrightnessShader;
    private Paint mSaturationPaint;
    private Paint mBrightnessPaint;
    private Paint mFramePaint;
    private Paint mPaint;
    private Paint mCenterPaint;
    private Paint mThumbPaint;
    private int mCurrentColor = 0xFFFF0000;
    private int mHueColor = 0xFFFF0000;
    private final int[] mColors = new int[]{0xFFFF0000, 0xFFFF00FF,
            0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00, 0xFFFF0000};

    private OnColorChangedListener mListener;

    private boolean mTrackingHue;
    private boolean mTrackingSaturation;
    private boolean mTrackingBrightness;

    private float mCircleWidth;
    private float mCenterRadius;
    private float mCenterX;
    private float mCenterY;
    private float mRadius;
    private float mSeekBarWidth;
    private float mCircleBarDis;
    private float mCircleSpace;

    private float mFrameWidth;
    private int mFrameColor = 0xffb9b9b9;
    private float mThumbRadius;
    private int mThumbColor = 0xffffffff;

    private RectF mRectFSaturation = new RectF();
    private RectF mRectFBrightness = new RectF();
    private RectF mRectFTemp = new RectF();
    private float[] mHSB = new float[3];
    private float[] mHSBTemp = new float[3];
    private float[] mCenterTemp = new float[2];

    public ColorPickerView(Context context) {
        super(context);

        init(context);
    }

    public ColorPickerView(Context context, AttributeSet attrs) {
        super(context, attrs);

        init(context);
    }

    public ColorPickerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        init(context);
    }

    public void setOnColorChangedListener(OnColorChangedListener l) {
        mListener = l;
    }

    public void setColor(int color) {
        mCurrentColor = color;
        Color.colorToHSV(color, mHSB);
        mHSBTemp[0] = mHSB[0];
        mHSBTemp[1] = 1.0f;
        mHSBTemp[2] = 1.0f;
        mHueColor = Color.HSVToColor(mHSBTemp);
        mCenterPaint.setColor(color);
        invalidate();
    }

    public void getCenter(float[] center) {
        center[0] = mCenterX;
        center[1] = mCenterY;
    }

    public float getRadius() {
        return mRadius - mFrameWidth;
    }

    private void init(Context context) {
        Shader s = new SweepGradient(0, 0, mColors, null);

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setShader(s);
        mPaint.setStyle(Paint.Style.STROKE);

        mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCenterPaint.setColor(mCurrentColor);

        mRectFSaturation.setEmpty();
        mRectFBrightness.setEmpty();

        mSaturationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mSaturationPaint.setStyle(Paint.Style.FILL);

        mBrightnessPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBrightnessPaint.setStyle(Paint.Style.FILL);

        final DisplayMetrics metrics = context.getResources()
                .getDisplayMetrics();
        mSeekBarWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                24, metrics);
        mCircleBarDis = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                24, metrics);
        mFrameWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
                metrics);
        mThumbRadius = mSeekBarWidth / 2 - mFrameWidth;
        mCircleWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16,
                metrics);
        mCircleSpace = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 11,
                metrics);

        mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mFramePaint.setStyle(Paint.Style.STROKE);
        mFramePaint.setStrokeWidth(mFrameWidth);
        mFramePaint.setColor(mFrameColor);

        mThumbPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mThumbPaint.setStyle(Paint.Style.FILL);
        mThumbPaint.setColor(mThumbColor);

        Color.colorToHSV(mCurrentColor, mHSB);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mRadius <= 0) {
            return;
        }

        if (!mRectFSaturation.isEmpty()) {
            createSaturationShader();
            canvas.drawRoundRect(mRectFSaturation, mSeekBarWidth / 2,
                    mSeekBarWidth / 2, mSaturationPaint);
            canvas.drawRoundRect(mRectFSaturation, mSeekBarWidth / 2,
                    mSeekBarWidth / 2, mFramePaint);

            getSaturationBarThumbCenter(mCenterTemp);
            canvas.drawCircle(mCenterTemp[0], mCenterTemp[1], mThumbRadius,
                    mThumbPaint);
            canvas.drawCircle(mCenterTemp[0], mCenterTemp[1], mThumbRadius,
                    mFramePaint);

            createBrightnessShader();
            canvas.drawRoundRect(mRectFBrightness, mSeekBarWidth / 2,
                    mSeekBarWidth / 2, mBrightnessPaint);
            canvas.drawRoundRect(mRectFBrightness, mSeekBarWidth / 2,
                    mSeekBarWidth / 2, mFramePaint);

            getBrightnessBarThumbCenter(mCenterTemp);
            canvas.drawCircle(mCenterTemp[0], mCenterTemp[1], mThumbRadius,
                    mThumbPaint);
            canvas.drawCircle(mCenterTemp[0], mCenterTemp[1], mThumbRadius,
                    mFramePaint);
        }

        final float r = mRadius - mCircleWidth * 0.5f - mFrameWidth;

        int saveCount = canvas.getSaveCount();

        canvas.translate(mCenterX, mCenterY);
        mPaint.setStrokeWidth(mCircleWidth);
        mRectFTemp.set(-r, -r, r, r);
        canvas.drawOval(mRectFTemp, mPaint);
        canvas.drawCircle(0, 0, mCenterRadius, mCenterPaint);

        getHueBarThumbCenter(mCenterTemp);
        canvas.drawCircle(mCenterTemp[0], mCenterTemp[1], mCircleWidth / 2,
                mThumbPaint);
        canvas.drawCircle(mCenterTemp[0], mCenterTemp[1], mCircleWidth / 2 - mFrameWidth / 2,
                mFramePaint);

        canvas.restoreToCount(saveCount);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
                            int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        //mCircleWidth = 0;
        mCenterRadius = 0;
        mCenterX = 0;
        mCenterY = 0;
        mRadius = 0;

        mRectFSaturation.setEmpty();
        mRectFBrightness.setEmpty();

        final int width = getWidth();
        final int height = getHeight();
        final int minLength = Math.min(width, height * 2);
        if (minLength < mSeekBarWidth * 2) {
            return;
        }

        mRadius = minLength * 0.25f;
        mCenterRadius = mRadius - mCircleWidth - mCircleSpace;
        if (mCenterRadius < 0)
            mCenterRadius = 0;
        mCenterX = mRadius;
        mCenterY = height * 0.5f;

        float barLength = width - mRadius * 2 - mCircleBarDis;
        if (barLength < mSeekBarWidth * 2) {
            return;
        }

        float x = mRadius * 2 + mCircleBarDis;
        float startY = mCenterY - mRadius;

        mRectFSaturation.left = x;
        mRectFSaturation.right = mRectFSaturation.left + barLength - mFrameWidth;
        mRectFSaturation.top = startY + (mRadius - mSeekBarWidth) / 2;
        mRectFSaturation.bottom = mRectFSaturation.top + mSeekBarWidth;

        mRectFBrightness.left = x;
        mRectFBrightness.right = mRectFBrightness.left + barLength - mFrameWidth;
        mRectFBrightness.top = startY + mRadius + (mRadius - mSeekBarWidth) / 2;
        mRectFBrightness.bottom = mRectFBrightness.top + mSeekBarWidth;
    }

    private void createSaturationShader() {
        if (mRectFSaturation.isEmpty()) {
            return;
        }

        int color0 = 0xFFFFFFFF;
        int color1 = mHueColor;

        mSaturationShader = new LinearGradient(mRectFSaturation.left,
                mRectFSaturation.top, mRectFSaturation.right,
                mRectFSaturation.bottom, new int[]{color0, color1}, null,
                TileMode.CLAMP);
        mSaturationPaint.setShader(mSaturationShader);
    }

    private void createBrightnessShader() {
        if (mRectFBrightness.isEmpty()) {
            return;
        }

        int color = 0xFFFFFFFF;

        mBrightnessShader = new LinearGradient(mRectFBrightness.left,
                mRectFBrightness.top, mRectFBrightness.right,
                mRectFBrightness.bottom, new int[]{Color.BLACK, color},
                null, TileMode.CLAMP);
        mBrightnessPaint.setShader(mBrightnessShader);
    }

    private void getSaturationBarThumbCenter(float[] center) {
        float width = mRectFSaturation.width();
        width -= mSeekBarWidth;
        center[0] = mRectFSaturation.left + mSeekBarWidth / 2 + width * mHSB[1];
        center[1] = mRectFSaturation.centerY();
        Log.d("tag", center[0] + "　" + center[1]);
    }

    private void getBrightnessBarThumbCenter(float[] center) {
        float width = mRectFBrightness.width();
        width -= mSeekBarWidth;
        center[0] = mRectFBrightness.left + mSeekBarWidth / 2 + width * mHSB[2];
        center[1] = mRectFBrightness.centerY();
    }

    private void getHueBarThumbCenter(float[] center) {
        double d = Math.PI * mHSB[0] / 180;
        float radius = mRadius - mCircleWidth / 2 - mFrameWidth;
        center[0] = (float) (radius * Math.cos(d));
        center[1] = (float) (-radius * Math.sin(d));
    }

    private int floatToByte(float x) {
        int n = Math.round(x);
        return n;
    }

    private int pinToByte(int n) {
        if (n < 0) {
            n = 0;
        } else if (n > 255) {
            n = 255;
        }
        return n;
    }

    private int ave(int s, int d, float p) {
        return s + Math.round(p * (d - s));
    }

    private int interpColor(int[] colors, float unit) {
        if (unit <= 0) {
            return colors[0];
        }
        if (unit >= 1) {
            return colors[colors.length - 1];
        }

        float p = unit * (colors.length - 1);
        int i = (int) p;
        p -= i;

        // now p is just the fractional part [0...1) and i is the index
        int c0 = colors[i];
        int c1 = colors[i + 1];
        int a = ave(Color.alpha(c0), Color.alpha(c1), p);
        int r = ave(Color.red(c0), Color.red(c1), p);
        int g = ave(Color.green(c0), Color.green(c1), p);
        int b = ave(Color.blue(c0), Color.blue(c1), p);

        return Color.argb(a, r, g, b);
    }

    private int rotateColor(int color, float rad) {
        float deg = rad * 180 / 3.1415927f;
        int r = Color.red(color);
        int g = Color.green(color);
        int b = Color.blue(color);

        ColorMatrix cm = new ColorMatrix();
        ColorMatrix tmp = new ColorMatrix();

        cm.setRGB2YUV();
        tmp.setRotate(0, deg);
        cm.postConcat(tmp);
        tmp.setYUV2RGB();
        cm.postConcat(tmp);

        final float[] a = cm.getArray();

        int ir = floatToByte(a[0] * r + a[1] * g + a[2] * b);
        int ig = floatToByte(a[5] * r + a[6] * g + a[7] * b);
        int ib = floatToByte(a[10] * r + a[11] * g + a[12] * b);

        return Color.argb(Color.alpha(color), pinToByte(ir), pinToByte(ig),
                pinToByte(ib));
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }

        if (mRadius <= 0) {
            return true;
        }

        float x = event.getX() - mCenterX;
        float y = event.getY() - mCenterY;

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                if (mListener != null) {
                    mListener.onStartDragging();
                }

                double dis = Math.sqrt(x * x + y * y);
                boolean inHue = (dis <= mRadius * 1.1) && (dis >= 0.5 * mRadius);
                boolean inSaturation = false;
                boolean inBrightness = false;

                if (!inHue && !mRectFSaturation.isEmpty()) {
                    float x0 = event.getX();
                    float y0 = event.getY();

                    mRectFTemp.set(mRectFSaturation);
                    mRectFTemp.inset(-mSeekBarWidth / 2, -mSeekBarWidth / 2);
                    if (mRectFTemp.bottom > mCenterY) {
                        mRectFTemp.bottom = mCenterY;
                    }
                    if (mRectFTemp.contains(x0, y0)) {
                        inSaturation = true;
                    }

                    if (!inSaturation) {
                        mRectFTemp.set(mRectFBrightness);
                        mRectFTemp.inset(-mSeekBarWidth / 2, -mSeekBarWidth / 2);
                        if (mRectFTemp.top < mCenterY) {
                            mRectFTemp.top = mCenterY;
                        }
                        if (mRectFTemp.contains(x0, y0)) {
                            inBrightness = true;
                        }
                    }
                }

                mTrackingHue = inHue;
                mTrackingSaturation = inSaturation;
                mTrackingBrightness = inBrightness;
            }
            case MotionEvent.ACTION_MOVE:
                if (mTrackingHue) {
                    float angle = (float) Math.atan2(y, x);
                    // need to turn angle [-PI ... PI] into unit [0....1]
                    float unit = angle / (2 * PI);
                    if (unit < 0) {
                        unit += 1;
                    }

                    int color = interpColor(mColors, unit);
                    mHueColor = color;
                    Color.colorToHSV(color, mHSBTemp);
                    mHSB[0] = mHSBTemp[0];
                    mCurrentColor = Color.HSVToColor(mHSB);
                    mCenterPaint.setColor(mCurrentColor);
                    invalidate();
                } else if (mTrackingSaturation) {
                    float xStart = mRectFSaturation.left + mSeekBarWidth / 2;
                    float xEnd = mRectFSaturation.right - mSeekBarWidth / 2;

                    x = event.getX();
                    if (x < xStart) {
                        x = xStart;
                    } else if (x > xEnd) {
                        x = xEnd;
                    }

                    mHSB[1] = (x - xStart) / (xEnd - xStart);
                    mCurrentColor = Color.HSVToColor(mHSB);
                    mCenterPaint.setColor(mCurrentColor);
                    invalidate();
                } else if (mTrackingBrightness) {
                    float xStart = mRectFBrightness.left + mSeekBarWidth / 2;
                    float xEnd = mRectFBrightness.right - mSeekBarWidth / 2;

                    x = event.getX();
                    if (x < xStart) {
                        x = xStart;
                    } else if (x > xEnd) {
                        x = xEnd;
                    }

                    mHSB[2] = (x - xStart) / (xEnd - xStart);
                    mCurrentColor = Color.HSVToColor(mHSB);
                    mCenterPaint.setColor(mCurrentColor);
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mListener != null) {
                    mListener.onStopDragging();
                }

                if (mTrackingHue || mTrackingSaturation || mTrackingBrightness) {
                    if (mListener != null) {
                        mListener.onColorChanged(mCurrentColor & 0x00ffffff);
                    }
                }
                mTrackingHue = false;
                mTrackingSaturation = false;
                mTrackingBrightness = false;
                break;
        }
        return true;
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void method1() {
        Log.d("tag", "method1");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void method2() {
        Log.d("tag", "method2");
    }
}
